//! HTTP protocol integration tests
//! 
//! Tests for the complete HTTP protocol support

use ai_test_platform::{
    models::{
        request::{HttpRequest, HttpMethod},
        step::StepContext,
        Value, ValueMap,
    },
    plugins::builtin::http::HttpPlugin,
    plugins::builtin::BuiltinPlugin,
    protocols::http::HttpProtocolHandler,
    protocols::ProtocolHandler,
    validation::{ValidationEngine, ValidationContext, assertions::*},
};
use serde_json::json;
use std::collections::HashMap;

#[tokio::test]
async fn test_http_protocol_handler_creation() {
    let handler = HttpProtocolHandler::new();
    assert_eq!(handler.protocol_name(), "http");

    let operations = handler.supported_operations();
    assert!(operations.contains(&"request".to_string()));
    assert!(operations.contains(&"get".to_string()));
    assert!(operations.contains(&"post".to_string()));
    assert!(operations.contains(&"put".to_string()));
    assert!(operations.contains(&"delete".to_string()));
}

#[tokio::test]
async fn test_http_protocol_handler_initialization() {
    let mut handler = HttpProtocolHandler::new();
    let mut config = HashMap::new();
    config.insert("timeout".to_string(), json!(30.0));
    config.insert("verify_ssl".to_string(), json!(false));
    config.insert("user_agent".to_string(), json!("AI-Test-Platform/1.0"));

    let result = handler.initialize(config).await;
    assert!(result.is_ok());
}

#[tokio::test]
async fn test_http_plugin_creation() {
    let plugin = HttpPlugin::new();

    // Should be able to create plugin
    assert!(plugin.functions().contains(&"request".to_string()));
    assert!(plugin.functions().contains(&"get".to_string()));
    assert!(plugin.functions().contains(&"post".to_string()));
}

#[tokio::test]
async fn test_http_request_validation() {
    let plugin = HttpPlugin::new();
    
    // Test valid request
    let valid_request = json!({
        "method": "GET",
        "url": "https://httpbin.org/get"
    });
    
    let result = plugin.call_function("validate_request", &[valid_request]).await.unwrap();
    assert_eq!(result["valid"], true);
    
    // Test invalid request (missing URL)
    let invalid_request = json!({
        "method": "GET"
    });
    
    let result = plugin.call_function("validate_request", &[invalid_request]).await.unwrap();
    assert_eq!(result["valid"], false);
    
    // Test invalid URL format
    let invalid_url_request = json!({
        "method": "GET",
        "url": "not-a-url"
    });
    
    let result = plugin.call_function("validate_request", &[invalid_url_request]).await.unwrap();
    assert_eq!(result["valid"], false);
}

#[tokio::test]
async fn test_http_request_structure() {
    // Test HttpRequest creation and serialization
    let request = HttpRequest::new(HttpMethod::GET, "https://httpbin.org/get".to_string());
    
    assert_eq!(request.method, HttpMethod::GET);
    assert_eq!(request.url, "https://httpbin.org/get");
    assert!(request.headers.is_empty());
    assert!(request.params.is_empty());
    
    // Test serialization
    let json_value = serde_json::to_value(&request).unwrap();
    assert!(json_value.is_object());
    assert_eq!(json_value["method"], "GET");
    assert_eq!(json_value["url"], "https://httpbin.org/get");
}

#[tokio::test]
async fn test_http_request_with_headers() {
    let mut request = HttpRequest::new(HttpMethod::POST, "https://httpbin.org/post".to_string());
    request.headers.insert("Content-Type".to_string(), "application/json".to_string());
    request.headers.insert("Authorization".to_string(), "Bearer token123".to_string());
    
    assert_eq!(request.headers.len(), 2);
    assert_eq!(request.headers.get("Content-Type"), Some(&"application/json".to_string()));
    assert_eq!(request.headers.get("Authorization"), Some(&"Bearer token123".to_string()));
}

#[tokio::test]
async fn test_http_request_with_json_body() {
    let mut request = HttpRequest::new(HttpMethod::POST, "https://httpbin.org/post".to_string());
    let json_body = json!({
        "name": "test",
        "value": 42
    });
    request.json = Some(json_body.clone());
    
    assert!(request.json.is_some());
    assert_eq!(request.json.unwrap(), json_body);
}

#[tokio::test]
async fn test_http_request_with_query_params() {
    let mut request = HttpRequest::new(HttpMethod::GET, "https://httpbin.org/get".to_string());
    request.params.insert("page".to_string(), Value::from("1"));
    request.params.insert("limit".to_string(), Value::from("10"));

    assert_eq!(request.params.len(), 2);
    assert_eq!(request.params.get("page"), Some(&Value::from("1")));
    assert_eq!(request.params.get("limit"), Some(&Value::from("10")));
}

#[tokio::test]
async fn test_http_methods_enum() {
    // Test all HTTP methods
    let methods = vec![
        HttpMethod::GET,
        HttpMethod::POST,
        HttpMethod::PUT,
        HttpMethod::DELETE,
        HttpMethod::PATCH,
        HttpMethod::HEAD,
        HttpMethod::OPTIONS,
        HttpMethod::TRACE,
        HttpMethod::CONNECT,
    ];
    
    for method in methods {
        let request = HttpRequest::new(method.clone(), "https://example.com".to_string());
        assert_eq!(request.method, method);
        
        // Test serialization
        let json_value = serde_json::to_value(&request).unwrap();
        assert!(json_value["method"].is_string());
    }
}

#[tokio::test]
async fn test_http_plugin_client_info() {
    let plugin = HttpPlugin::new();
    
    let result = plugin.call_function("client_info", &[]).await.unwrap();
    
    assert!(result.is_object());
    assert_eq!(result["name"], "reqwest");
    assert!(result["features"].is_array());
    
    let features = result["features"].as_array().unwrap();
    assert!(features.contains(&json!("json")));
    assert!(features.contains(&json!("multipart")));
}

#[tokio::test]
async fn test_http_plugin_functions_list() {
    let plugin = HttpPlugin::new();
    let functions = plugin.functions();
    
    // Check that all expected functions are available
    let expected_functions = vec![
        "request", "get", "post", "put", "delete", 
        "patch", "head", "options", "client_info", 
        "configure", "validate_request"
    ];
    
    for func in expected_functions {
        assert!(functions.contains(&func.to_string()), 
                "Missing function: {}", func);
    }
}

#[tokio::test]
async fn test_http_request_timeout() {
    let mut request = HttpRequest::new(HttpMethod::GET, "https://httpbin.org/delay/1".to_string());
    request.timeout = Some(5.0); // 5 seconds timeout
    
    assert_eq!(request.timeout, Some(5.0));
    
    // Test serialization includes timeout
    let json_value = serde_json::to_value(&request).unwrap();
    assert_eq!(json_value["timeout"], 5.0);
}

#[tokio::test]
async fn test_http_request_builder_pattern() {
    // Test that we can build requests in a fluent style
    let mut request = HttpRequest::new(HttpMethod::POST, "https://httpbin.org/post".to_string());
    
    // Add headers
    request.headers.insert("Content-Type".to_string(), "application/json".to_string());
    
    // Add JSON body
    request.json = Some(json!({"test": "data"}));
    
    // Add timeout
    request.timeout = Some(10.0);
    
    // Verify all properties are set
    assert_eq!(request.method, HttpMethod::POST);
    assert_eq!(request.url, "https://httpbin.org/post");
    assert_eq!(request.headers.len(), 1);
    assert!(request.json.is_some());
    assert_eq!(request.timeout, Some(10.0));
}

#[tokio::test]
async fn test_http_plugin_error_handling() {
    let plugin = HttpPlugin::new();
    
    // Test calling function with invalid arguments
    let result = plugin.call_function("get", &[]).await;
    assert!(result.is_err());
    
    // Test calling non-existent function
    let result = plugin.call_function("non_existent", &[]).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_http_request_deserialization() {
    let json_str = r#"{
        "method": "POST",
        "url": "https://api.example.com/users",
        "headers": {
            "Content-Type": "application/json",
            "Authorization": "Bearer token123"
        },
        "json": {
            "name": "John Doe",
            "email": "john@example.com"
        },
        "timeout": 30.0
    }"#;
    
    let request: HttpRequest = serde_json::from_str(json_str).unwrap();
    
    assert_eq!(request.method, HttpMethod::POST);
    assert_eq!(request.url, "https://api.example.com/users");
    assert_eq!(request.headers.len(), 2);
    assert!(request.json.is_some());
    assert_eq!(request.timeout, Some(30.0));
}

/// Test HTTP protocol handler with validation
#[tokio::test]
async fn test_http_protocol_handler() {
    let mut handler = HttpProtocolHandler::new();

    // Initialize handler
    let config = HashMap::new();
    let result = handler.initialize(config).await;
    assert!(result.is_ok(), "Handler initialization should succeed");

    // Test protocol name
    assert_eq!(handler.protocol_name(), "http");

    // Test supported operations
    let operations = handler.supported_operations();
    assert!(operations.contains(&"request".to_string()));
    assert!(operations.contains(&"get".to_string()));
    assert!(operations.contains(&"post".to_string()));
}

/// Test validation engine functionality
#[test]
fn test_validation_engine() {
    let engine = ValidationEngine::new();

    // Create test validation context
    let mut response_data = HashMap::new();
    response_data.insert("status_code".to_string(), json!(200));
    response_data.insert("body".to_string(), json!({
        "message": "success",
        "data": {
            "id": 123,
            "name": "test"
        }
    }));

    let context = ValidationContext::new(response_data, HashMap::new());

    // Test basic value access
    assert_eq!(context.get_response_value("status_code"), Some(&json!(200)));
    assert_eq!(context.get_response_value("body.message"), Some(&json!("success")));
    assert_eq!(context.get_response_value("body.data.id"), Some(&json!(123)));
}

/// Test assertion builder patterns
#[test]
fn test_assertion_patterns() {
    // Test OK response assertion
    let ok_assertion = CommonAssertions::ok();
    let (validators, _) = ok_assertion.build();
    assert!(!validators.is_empty(), "OK assertion should have validators");

    // Test authentication response assertion
    let auth_assertion = CommonAssertions::auth_response();
    let (validators, extractors) = auth_assertion.build();
    assert!(!validators.is_empty(), "Auth assertion should have validators");
    assert!(!extractors.is_empty(), "Auth assertion should have extractors");

    // Test error response assertion
    let error_assertion = CommonAssertions::error_response();
    let (validators, _) = error_assertion.build();
    assert!(!validators.is_empty(), "Error assertion should have validators");

    // Test custom assertion building
    let custom_assertion = AssertionBuilder::new()
        .status_code(201)
        .json_path_equals("body.status", json!("created"))
        .extract_json_path("body.id", "created_id");

    let (validators, extractors) = custom_assertion.build();
    assert!(!validators.is_empty(), "Custom assertion should have validators");
    assert!(!extractors.is_empty(), "Custom assertion should have extractors");
}

/// Test HTTP parameter validation
#[test]
fn test_http_parameter_validation() {
    let handler = HttpProtocolHandler::new();

    // Test valid parameters
    let mut valid_params = HashMap::new();
    valid_params.insert("url".to_string(), json!("https://example.com/api"));

    let result = handler.validate_params("get", &valid_params);
    assert!(result.is_ok(), "Valid parameters should pass validation");

    // Test missing URL
    let empty_params = HashMap::new();
    let result = handler.validate_params("get", &empty_params);
    assert!(result.is_err(), "Missing URL should fail validation");

    // Test invalid URL scheme
    let mut invalid_params = HashMap::new();
    invalid_params.insert("url".to_string(), json!("ftp://example.com"));

    let result = handler.validate_params("get", &invalid_params);
    assert!(result.is_err(), "Invalid URL scheme should fail validation");

    // Test invalid URL format
    let mut malformed_params = HashMap::new();
    malformed_params.insert("url".to_string(), json!("not-a-url"));

    let result = handler.validate_params("get", &malformed_params);
    assert!(result.is_err(), "Malformed URL should fail validation");
}

// Note: This test is commented out because it depends on external network access
// which may not be available in all testing environments
#[tokio::test]
#[ignore]
async fn test_http_get_request_execution() {
    let mut handler = HttpProtocolHandler::new();
    let config = HashMap::new();
    handler.initialize(config).await.unwrap();

    let mut params = HashMap::new();
    params.insert("url".to_string(), json!("https://httpbin.org/get"));
    params.insert("headers".to_string(), json!({
        "User-Agent": "AI-Test-Platform/1.0"
    }));

    let mut context = StepContext::new(uuid::Uuid::new_v4(), uuid::Uuid::new_v4());
    let result = handler.execute("get", params, &mut context).await;

    assert!(result.is_ok());
    let step_result = result.unwrap();
    assert!(step_result.success);
    assert!(step_result.elapsed > 0);
    assert!(step_result.http_stat.is_some());

    let http_stat = step_result.http_stat.unwrap();
    assert_eq!(http_stat.get("status_code"), Some(&200));
}

// Note: This test is commented out because it depends on external network access
// which may not be available in all testing environments
#[tokio::test]
#[ignore]
async fn test_http_post_request_execution() {
    let mut handler = HttpProtocolHandler::new();
    let config = HashMap::new();
    handler.initialize(config).await.unwrap();

    let mut params = HashMap::new();
    params.insert("url".to_string(), json!("https://httpbin.org/post"));
    params.insert("json".to_string(), json!({
        "test": "data",
        "number": 42
    }));
    params.insert("headers".to_string(), json!({
        "Content-Type": "application/json"
    }));

    let mut context = StepContext::new(uuid::Uuid::new_v4(), uuid::Uuid::new_v4());
    let result = handler.execute("post", params, &mut context).await;

    assert!(result.is_ok());
    let step_result = result.unwrap();
    assert!(step_result.success);

    let http_stat = step_result.http_stat.unwrap();
    assert_eq!(http_stat.get("status_code"), Some(&200));
}
